package org.filecollector.filecontroller.virtualcatalogue;

import junit.framework.TestCase;
import org.apache.commons.lang3.ArrayUtils;
import org.filecollector.filecontroller.virtualcatalogue.virtualfile.*;
import org.junit.Test;

import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;

public class TestVirtualCatalogueSM {
    public static VirtualFile constructTestVF() {
        File file;
        try {
            file = File.createTempFile("111", "");
            file.deleteOnExit();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        VirtualFileDao dao = new VirtualFileDao(
                file,
                111L
        );
        VirtualFileDependencies dependencies = VirtualFileDependencies.getTest();
        return VirtualFilesFactory.constructSpecialVF(dao, dependencies);
    }

    @Test
    public void TestCheckWriteability() {
        File dir;
        try {
            dir = Files.createTempDirectory("testDir").toFile();
            dir.deleteOnExit();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        dir.setWritable(true);
        TestCase.assertTrue(
                VirtualCatalogueStaticMethods.checkAndFixWriteability(
                        dir, (String _) -> {}
                )
        );
    }

    @Test
    public void TestCheckReadability(){
        File dir;
        try {
            dir = Files.createTempDirectory("testDir").toFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        dir.setReadable(true);
        TestCase.assertTrue(
                VirtualCatalogueStaticMethods.checkAndFixReadability(
                        dir, (String _) -> {}
                )
        );
    }

    @Test
    public void TestTroublesHunt() {
        var e = new InvalidFileException("bla-bla", FileTroubles.UNKNOWN_TROUBLES);
        var result = new ArrayList<InvalidFileException>(1);
        VirtualCatalogueStaticMethods.troubleHunt(() -> {throw e;}, result::add);
        TestCase.assertEquals(e.getReason(), result.getFirst().getReason());
    }

    @Test
    public void TestPreventThrowing() {
        try {
            VirtualCatalogueStaticMethods.preventThrowing(
                    () -> {throw new RuntimeException();},
                    (String _) -> {},
                    () -> 5
            );
        } catch (RuntimeException e) {
            TestCase.fail();
        }
    }

    @Test
    public void TestCheckingDirectory() {
        File dir;
        try {
            dir = Files.createTempDirectory("testDir").toFile();
            dir.deleteOnExit();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        dir.setReadable(true);
        dir.setWritable(true);
        TestCase.assertTrue(VirtualCatalogueStaticMethods.checkDirectory(dir, (String _) -> {}, (String _) -> {}));
        dir.delete();
        TestCase.assertFalse(VirtualCatalogueStaticMethods.checkDirectory(dir, (String _) -> {}, (String _) -> {}));
    }

    @Test
    public void TestGetPath() {
        var vf = constructTestVF();
        var data = vf.getDao();
        TestCase.assertEquals(
                VirtualCatalogueStaticMethods.getPath(
                        data.id(),
                        () -> vf,
                        (InvalidFileException _) -> {},
                        (String _) -> {}
                ).get(),
                data.innerFile().toPath()
        );
    }

    @Test
    public void TestGetCheckSum() {
        var vf = constructTestVF();
        var data = vf.getDao();
        TestCase.assertEquals(
                VirtualCatalogueStaticMethods.getCheckSum(
                        data.id(),
                        () -> vf,
                        (InvalidFileException _) -> {},
                        (String _) -> {}
                ).getAsLong(),
                0L
        );
    }

    @Test
    public void TestReadDirectly() {
        var vf = constructTestVF();
        char[] exp = {'f', 'u', 'c', 'k'};
        char[] ret;
        var data = vf.getDao();
        var file = data.innerFile();
        try (FileWriter writer = new FileWriter(file)) {
            writer.write(exp);
        } catch (Exception e) {
            throw new RuntimeException();
        }
        ret = VirtualCatalogueStaticMethods.readDirectly(
                data.id(),
                exp.length,
                () -> vf,
                (InvalidFileException _) -> {},
                (String _) -> {}
        ).get();
        int i = 0;
        for (var ch : exp) {
            TestCase.assertEquals(ch, ret[i]);
            i++;
        }
    }

    @Test
    public void TestWriteDirectly() {
        var vf = constructTestVF();
        char[] exp = {'f', 'u', 'c', 'k'};
        char[] ret = new char[exp.length];
        var data = vf.getDao();
        VirtualCatalogueStaticMethods.writeDirectly(
                data.id(),
                exp,
                () -> vf,
                false,
                (InvalidFileException _) -> {},
                (String _) -> {}
        );
        var file = data.innerFile();
        try(FileReader reader = new FileReader(file)) {
            reader.read(ret);
        } catch (Exception e) {
            throw new RuntimeException();
        }
        int i = 0;
        for (var ch : exp) {
            TestCase.assertEquals(ch, ret[i]);
            i++;
        }
        char[] add = {'a', 'd', 'd'};
        char[] exp2 = ArrayUtils.addAll(exp, add);
        char[] ret2 = new char[exp2.length];
        VirtualCatalogueStaticMethods.writeDirectly(
                data.id(),
                exp2,
                () -> vf,
                false,
                (InvalidFileException _) -> {},
                (String _) -> {}
        );
        try(FileReader reader = new FileReader(file)) {
            reader.read(ret2);
        } catch (Exception e) {
            throw new RuntimeException();
        }
        i = 0;
        for (var ch : exp2) {
            TestCase.assertEquals(ch, ret2[i]);
            i++;
        }
    }

    @Test
    public void testRemove() {
        var vf = constructTestVF();
        VirtualCatalogueStaticMethods.remove(() -> vf);
        TestCase.assertFalse(vf.getDao().innerFile().exists());
    }

    @Test
    public void testFindFreeDiapason() {
        var rg = new Random();
        rg.setSeed(System.currentTimeMillis());
        HashSet<Long> freeIds = new HashSet<>();
        HashSet<Long> occupiedIds = new HashSet<>();
        Long minId = rg.nextLong(0x1FFFF);
        Long maxId = minId + Math.abs(rg.nextLong(0x1FFFF));
        for (int i = 0; i < maxId - minId; i++) {
            freeIds.add(minId + rg.nextLong(maxId - minId));
        }
        for (Long i = minId; i <= maxId; i++) {
            if (!freeIds.contains(i)) {
                occupiedIds.add(i);
            }
        }
        var ret = VirtualCatalogueStaticMethods.findFreeDiapasons(minId, maxId, occupiedIds);
        for (var id : occupiedIds) {
            boolean check = false;
            for (var retDiapason : ret) {
                if (retDiapason[0] >= id || id <= retDiapason[1]) {
                    check = true;
                    break;
                }
            }
            TestCase.assertTrue(check);
        }
    }
}
